#include "amever.h"

struct buffer *buffer_init (int size) {
	struct buffer* me = (struct buffer *)calloc(1, sizeof(struct buffer));
	if (size<=0)
		size = BUF_SIZE;
	else if (size > MAX_BUF_SIZE) {
		return 0;
	}
	me->reallocNeeded = 0;
	me->data = (char *)calloc(size, 1);
	me->size = size;
	me->mark = 0;
	me->head = 0;
	return me;
}

inline int buffer_read (struct buffer *me, int fd) {
	int size = read(fd, me->data + me->mark, me->size-me->mark);
	if (size <= 0) {
		amelog("%d read from %d, no good\n", size, fd);
		return -1;
	}
	me->mark += size;
	if ((me->mark - me->size) < 16) {
		if (buffer_realloc(me, (me->size/16)+16)) {
			amelog("Buffer max reached reading to buffer.\n");
			return -1;
		}
	}
	me->data[me->mark] = '\0';
	return size;
}

int buffer_readBytes (struct buffer *me, int fd, int max) {
	if (max > me->size-me->head)
		max = me->size-me->head;
	int size;
	if ((size = read(fd, me->data + me->head, me->head)) >= me->size - (me->mark+1)) {
		// Buffer's full? Give it some more memory, if it doesn't grow bigger than MAX_BUF_SIZE
		if (me->size < MAX_BUF_SIZE) {
	 	buffer_realloc(me, (int)(me->size / 16)+16);
		}
		else {
			// This should shut the thing using buffer.
			return -1;
		}
	}

	me->mark += size;
	me->data[me->mark] = '\0';
	return (size);
}

int buffer_set(struct buffer *me, const char *str) {
	buffer_blank(me);
	int new_size = strlen(str);
	if (new_size > MAX_BUF_SIZE) {
		return 1;
	}
	else if (strlen(str) > me->size) {
		buffer_realloc(me, new_size+1);
	}
	strcpy(me->data, str);
	me->mark = new_size;
	return 0;
}

int buffer_write(struct buffer *me, int fd) {
	if (me == 0 || me->data == 0) // This check should be performed in each buffer_ operation.
		return -1;
	int size = write(fd, me->data, me->mark > MAX_SOCKET_DATA ? MAX_SOCKET_DATA : me->mark );
	if (size < me->mark) {
		// We've written whatever was in memory from data to data+size
		// Now what we have to do is to move data not used yet to data+0 position, ovewriting used data.
		// Data before move: [000XXXXXX___] Data after  move: [XXXXXX______]
		// being 0 already written data (past data+size), X data not written yet, _ unusuble data (past mark)
		me->data = (char *)memmove(me->data, me->data+size, me->mark - size);
		me->mark -= size;
		me->data[me->mark] = '\0';
	}
	else {
		// Size equals means that we wrote everything we had in
		// buffer. The only thing we have to do now is to set mark to 0 so the
		// next read begins to overwrite the used data without moving anything.
		me->mark = 0;
	}
	return size;
}

int buffer_move(struct buffer *me, int pos) {
	// Similar to above
	if (pos > me->mark)
		return -1;
	memmove(me->data, me->data+pos, me->mark - pos);
	me->mark -= pos;
	me->data[me->mark] = '\0';
	return 0;
}

void buffer_blank (struct buffer *me) {
      me->head = 0;
      me->mark = 0;
      memset(me->data, 0, me->size);
}

void buffer_shut (struct buffer *me) {
	if (me->data != 0) {
		free(me->data);
	}
	me->data = 0;
	me->size = 0;
	me->mark = 0;
	me->head = 0;
	if (me)
		free(me);
}

// XXX Useless?
int buffer_cpy (struct buffer *to, struct buffer *what) {
	amelog("reallocking buffer\n");
	int trailing_size;
	if ((trailing_size = ( (what->mark - what->head) -  ( to->size - to->mark ) ))>0) {
		if (buffer_realloc(to, trailing_size)) {
			amelog("Couldn't reallocate memory for buffer.\n");
			return -1;
		}
	}
	memcpy( (to->data + to->mark), (what->data + what->head), (what->mark - what->head) );
}

int buffer_realloc (struct buffer *me, int size) {
	if (!me->data)
		return 1;
	
	amelog("In buffer realloc\n");
	if (me->size > MAX_BUF_SIZE) {
		amelog("buffer_realloc: MAX_BUF_SIZE reached, cannot realloc\n");
		return 1;
	}
	if (size <= 1)
		size = BUF_SIZE;

	me->size += size;
	if ((me->data = realloc(me->data, me->size))==NULL) {
		amelog("Outa memory in buffer_realloc. Bad.\n");
		return 1;
	}
	// memset(me->data + (me->size-size),0,size);
	me->reallocNeeded = 0;
	return 0;
}
